package com.baizhi.cmfz.aspact;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 *实现缓存 利用的AOP的定义增强，确定需要缓存的位置
 * 然后使用Springboot操作redis的方式完成缓存
 */
@Configuration
@Aspect
public class CacheAspact {
    @Autowired
    private RedisTemplate redisTemplate;
    //这个类中需要有两个方法，一个是添加缓存，一个是执行数据库执行事务性操作时的删除缓存的方法
    /**
     *添加缓存
     *
     * 切注解
     *
     * 思路；选用环绕增强，
     * 先判断请求方法时，缓存中是不是存在这个数据
     * 当缓存中已经存在时，从缓存中返回方法的返回值数据
     * 当缓存中不存在要请求的方法返回值的数据时，放行，执行方法到数据库中查询
     *
     * 添加缓存选用类名作为redis的key 数据类型选用hash map的键是方法名加参数
     * 值是方法的返回值
     */
    /**
     * 返回值是对象，也就是查询的结果
     *ProcceedingJoinPoint 表示切入点连接对象可以通过这个对象获取方法签名
     * 然后获取到类名
     * @return
     */
    @Around("@annotation(com.baizhi.cmfz.annotation.AddCacheAnnotation)")
    public Object addCache(ProceedingJoinPoint joinPoint) throws Throwable {
        //System.out.println("添加缓存");
        //获取到类名以判断是否添加了缓存，类名作为hash类型值的key
        String className = joinPoint.getSignature().getClass().getName();
        System.out.println("类名"+className);
        //还需要获取到方法名和方法参数，这两个连接作为hash类型中值map中的key
        StringBuffer stringBuffer = new StringBuffer();
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        stringBuffer.append(methodName);
        //获取方法参数
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            stringBuffer.append(arg);
        }
        String hashKey = stringBuffer.toString();
        System.out.println("值map的键"+hashKey);
        //需要判断是不是已经有缓存数据了，如果有了缓存数据，直接返回缓存数据
        //如果没有缓存数据，需要放行查询数据库，然后将查询到的放进数据库，返回查询数据
        HashOperations hashOperations = redisTemplate.opsForHash();
        Object value = hashOperations.get(className, hashKey);
        if (value == null) {
            //没有的话，去数据库执行查询，添加缓存
            // ProceedingJoinPoint可以放行去执行切入点的方法
            value = joinPoint.proceed();
            Map map = new HashMap();
            map.put(hashKey,value);
            hashOperations.putAll(className,map);
            //设置失效时间 参数一要设置的key，参数2是时间，参数3是的时间单位
            redisTemplate.expire(className,24*60*60, TimeUnit.SECONDS);
        }else {
            System.out.println("缓存中已经有了，缓存取值");
        }
        //如果存在就缓存取值
        return value;
    }


    /**
     * 删除方法，
     * 切注解 ，当执行增删改时，删除缓存
     * 删除缓存的标准时清除该类的缓存
     * 执行前删除，所以使用前置增强
     */
    @Before("@annotation(com.baizhi.cmfz.annotation.DeleteCacheAnnotation)")
    public void deleteCache(JoinPoint joinPoint) {
        System.out.println("删除缓存");
        //获取类名
        String className = joinPoint.getSignature().getClass().getName();
        System.out.println("要删除的类名"+className);
        //删除缓存
        redisTemplate.delete(className);
    }
}
